#ifndef __AEXUDP_H__
#define __AEXUDP_H__

#include <stdlib.h>
#include "ringBuffer.h"

#define INVAILDSOCKET -1

#define USE_AEX_UDP

#define		AEXUDP_BEGIN	(0xFB)
#define		AEXUDP_END		(0xFE)

#define		AEXUDP_EBEGIN	(0xEB)
#define		AEXUDP_EEND		(0xEE)

#define		AEXUDP_GETBEGIN(x)				((x==0)?AEXUDP_BEGIN:AEXUDP_EBEGIN)	//根据是否启用加密参数确定包头
#define		AEXUDP_GETEND(x)				((x==0)?AEXUDP_END:AEXUDP_EEND)	//根据是否启用加密参数确定包尾

//|FB|INDEX|LEN|CMD|DATA|CRC|FE|
#define		AEXUDP_PACKNET_PRESIZE			6				//协议数据包的基础大小，即data区为0时的数据包大小
#define		AEXUDP_PACKNET_LENINDEX			2

#define		AEXUDP_PACKNET_MAX_PLOAD		250				//最大荷载数据
#define		AEXUDP_UART_MAX_LEN				100 			//串口最大数据长度
//获得起始标志
#define		AEXUDP_GET_BEGIN(buf)			(buf[0])
//获得荷载数据的长度
#define		AEXUDP_GET_INDEX(buf)			(buf[1])
#define		AEXUDP_GET_LEN(buf)				(buf[2])
#define		AEXUDP_GET_CMD(buf)				(buf[3])
//获得数据的起始地址
#define		AEXUDP_GET_DATA(buf)			(&buf[4])
#define		AEXUDP_GET_CRCDATA(buf)			(&buf[1])
#define		AEXUDP_GET_CRCLEN(buf)			(buf[2]+3)
//获得信令CRC值
#define		AEXUDP_GET_CRC(buf)				(buf[AEXUDP_GET_LEN(buf)+4])
//获得结束标志
#define		AEXUDP_GET_END(buf)				(buf[AEXUDP_GET_LEN(buf)+5])
//获得信令参数的长度
#define		AEXUDP_GET_PACKET_LEN(buf)		(AEXUDP_GET_LEN(buf)+AEXUDP_PACKNET_PRESIZE)		

#define		AEXUDP_SET_BEGIN(buf,b)			{buf[0]=b&0xFF;}
#define		AEXUDP_SET_INDEX(buf,val)		{buf[1]=val&0xFF;}			//设置数据包的序号
#define		AEXUDP_SET_LEN(buf,val)			{buf[2]=val&0xFF;}			//设置荷载数据的长度
#define		AEXUDP_SET_CMD(buf,val)			{buf[3]=val&0xFF;}
#define		AEXUDP_SET_DATA(buf,data,len)	{buf[2]=(len)&0xFF;memcpy(AEXUDP_GET_DATA(buf),data,len);}		
#define		AEXUDP_SET_CRC(buf)				{buf[buf[2]+4]=crc8(AEXUDP_GET_CRCDATA(buf),AEXUDP_GET_CRCLEN(buf));}		//获得数据的CRC值
#define		AEXUDP_SET_END(buf,e)			{buf[buf[2]+5]=e&0xFF;}

#define		AEXUDP_DATA_GET_DST(data)			(data[0])		//接受数据包的设备地址
#define		AEXUDP_DATA_GET_SRC(data)			(data[1])		//产生数据包的设备地址
#define		AEXUDP_DATA_GET_PAYLOAD(data)		(&data[2])		
#define		AEXUDP_DATA_GET_PAYLOADINDEX()		2		

#define		AEXUDP_DATA_SET_DST(data,val)				{data[0]=val;}		//接受数据包的设备地址
#define		AEXUDP_DATA_SET_SRC(data,val)				{data[1]=val;}		//产生数据包的设备地址
#define		AEXUDP_DATA_SET_PAYLOAD(data,buf,len)		{memcpy(AEXUDP_DATA_GET_PAYLOAD(data),buf,len);}		

#ifdef USE_AEX_UDP

/*
| 包头1B                   | 序号 | 长度1B | 命令1B | 命令参数数据nB | CRC1B  | 包尾1B |
| ----------------------- | ---- | -------- | -------- | :--------------- | -------- | -------- |
| FB                       | INDEX | LEN    | CMD    | DATA           | 校验和 | FE     |
| LEN=DATA的长度           |        |        |                |        |        |
| 校验和=CRC(INDEX+LEN+CMD+DATA) |        |        |                |        |        |

请求和回复的数据包格式相同。该协议可以通过串口、UDP/TCP网络、MQTT等传输，协议为净荷载数据。
注 ： 新增目标和源设备地址的字段，设备地址是针对智能网关本地端的，用1字节表述。
	1. 设备地址0xFF表示广播地址，设备地址0表示需要网关处理（网关路由在边缘计算模块上实现）的命令；
	2. 任何模块发送数据前先拉低Heart，发送完数据后再拉高，如果Heart已经是低电平，则等待Heart拉高后再发送；


| 命令字 | 说明                                                                               |
| -------- | ------------------------------------------------------------------------------------ |
| 0x01   | Modbus协议透传，命令的DATA部分作为一条Modbus命令来解析                             |
| 0x02   | 设备心跳报告                                                                       |
| 0x03   | 设备事件查询指令                                                                   |
| 0x10   | 开关量设备状态报告，可以分256组，每组可以有16路输出和16路输入                      |
| 0x11   | 模拟量输入报告，可以分256组，每组8路输入信息，每路输入信息32位(4个字节)            |
| 0x20   | 开关量设置输出状态，可以单设也可以批量设置，可以设置延时                           |
| 0x21   | 开关量设置输出状态反转，可以单设也可以批量设置，可以设置延时                       |
| 0x22   | 模拟量设置输出，可以单设，也可以批量设置，可以设置延时                             |
| 0x30   | 设置定时任务，任务从服务端下载shell脚本，使用定时器操作                            |
| 0x31   | 设置定时任务，定时执行任务为本协议的指令数据包，模块会在定时时间相当于收到该数据包 |
| 0x40   | 报告数字输入变化事件                                                               |
| 0x41   | 报告模拟输入变化事件                                                               |
| 0x42   | 模块告警                                                                           |
| 0x60   | RS232/RS485/CAN/W24(无线)/HART透传数据                                             |
*/

typedef int (*CMDFUNC)(unsigned char *buf,int len);
typedef struct {
	unsigned char start;				//数据包前缀
	unsigned char end;
	CMDFUNC func;
	CMDFUNC encrypt;
}PKTPROPO,*PPKTPROPO;

typedef enum{
	AEX_CMD_MODBUS = 0x01,				//Modbus协议透传，命令的DATA部分作为一条Modbus命令来解析
	AEX_CMD_HEART = 0x02,				//设备心跳报告
	AEX_CMD_REPORT_EVENT = 0x03,		//设备事件查询指令
	AEX_CMD_IOSTATUS = 0x10,			//请求设备IO状态，向服务器报告IO状态，可以作为心跳上报的数据
	AEX_CMD_REPORT_AIN = 0x11,			//模拟量输入报告，可以分256组，每组8路输入信息，每路输入信息32位(4个字节)
	AEX_CMD_DOSET = 0x20,				//设置输出状态，可以单设，也可以批量设置，依据掩码实现
	AEX_CMD_DOINVERT = 0x21,			//设置输出反转，可以单设，也可以批量设置，依据掩码实现
	AEX_CMD_AOSET = 0x22,				//模拟量设置输出，可以单设，也可以批量设置，可以设置延时
	AEX_CMD_SETTIMER = 0x30,			//终端通过从服务器下载shell脚本到机器，然后设置crontab任务来执行此脚本。
	AEX_CMD_DELTIMER = 0x31,			//删除定时任务
	AEX_CMD_DIEVENT = 0x40,				//报告输入变化
	AEX_CMD_AINEVENT = 0x41,			//报告模拟输入变化事件 
	AEX_CMD_DIEVENT_ONE = 0x45,			//报告输入变化
	AEX_CMD_AINEVENT_ONE = 0x46,		//报告模拟输入变化事件 
	AEX_CMD_ALERT = 0x4A,				//模块告警 
	AEX_CMD_DOLUAFUNC = 0x50,			//执行 LUA 的功能函数，服务端收到则为函数执行的返回值 
	AEX_CMD_TRANSDATA = 0x60,			//RS232/RS485/CAN/W24(无线)/HART透传数据

	AEX_CMD_MODDECT_EVENT=0x70,			//底板报告检测到模块变化消息
	AEX_CMD_MODCFG_WRITE=0x71,			//模块参数配置
	AEX_CMD_MODCFG_READ=0x72,			//模块参数读取
	AEX_CMD_MOD_RESET=0x73,				//模块重启

	AEX_CMD_MODMSG=0x80,				//加热服务的消息
}LIST_AEX_CMD;

// unsigned char sgw_get_mod_addr();

PPKTPROPO aexudp_get_pktpropo(unsigned char *buf);
int aexudp_find_begin(unsigned char *buf);
int aexudp_check_pktpropo(unsigned char *buf);
void aexudp_encode_packnet(unsigned char *buf,unsigned char src,unsigned char dst,unsigned char cmd,unsigned char *data,unsigned char len);
// void aexudp_buid_packnet(unsigned char *buf,unsigned char src,unsigned char dst,unsigned char cmd,unsigned char len);
unsigned char aexudp_get_packnet_index();

int aexudp_cmd_parse(unsigned char *buf,int len);
int aexudp_recv_from_udp(int udpFd,unsigned char *buf,unsigned int len);
int parse_packnet_cmd(PRingBuffer ringBuf,unsigned char *buf, size_t len);
void aexudp_buid_heart_packnet();
PPKTPROPO aexudp_get_default_prkpropo();
#endif  //USE_AEX_UDP



#endif  //__AEXUDP_H__
